#! -*- coding:utf-8 -*-
import json
import logging
import sys
from datetime import datetime, timedelta

from django.core.management.base import BaseCommand

from common.cache import redis_cache
from common.channel.model import Channel
from common.order.model import PAY_STATUS
from common.order.model import PayOrder
from common.utils import exceptions as err
from common.utils import telegram
from common.utils.send_p import ADMIN

reload(sys)
sys.setdefaultencoding('UTF-8')

_LOGGER = logging.getLogger(__name__)

BLACK_MCHS = [6001016, 6001019]

AVAILABLE_SERVICE_MAP = [
    "alipay", "quota_alipay", "hydra", "quota_hydra", "cloud_flash", "qq", "wechat", "quota_wxpay",
    "unionpay"
]

MONITOR_MCH = {
    # '6001022': '泰坦',
    # '6001017': '战神',
    '6001011': '凯撒',
    '6001000': 'witch',
    '6001015': 'loki'
}

# jp_monitor group
# _TG_FINANCIAL_RISK = -301835231
# my test group
# _TG_FINANCIAL_RISK = -261523497
# 财务告警group
_TG_FINANCIAL_RISK = -1001331494801

NOTIFY_UIDS = [
    ADMIN.KAEL,
    ADMIN.MIKE,
    ADMIN.ARNO,
    ADMIN.OWEN,
]
GROUP_ID = 4335


def _check_recently_30_mins_risk_report(channel_id):
    # 每30分钟指定通道告警。同比上一个30分钟的异常告警（附上项目、支付形式、通道id名称、异常数据和原因）
    # 1.持续30min无成功订单
    # 2.成功率近30min低于该项目该支付形式平均值5%
    last_thirty_mins_start = datetime.utcnow() + timedelta(minutes=-60)
    last_thirty_mins_end = datetime.utcnow() + timedelta(minutes=-30)

    success_count = PayOrder.query.filter(PayOrder.channel_id == channel_id).filter(
        PayOrder.status == PAY_STATUS.SUCC).filter(PayOrder.created_at >= last_thirty_mins_end).count()
    total_count = PayOrder.query.filter(PayOrder.channel_id == channel_id).filter(
        PayOrder.created_at >= last_thirty_mins_end).count()

    last_thirdy_mins_success_count = PayOrder.query.filter(PayOrder.channel_id == channel_id).filter(
        PayOrder.status == PAY_STATUS.SUCC).filter(PayOrder.created_at >= last_thirty_mins_start).filter(
        PayOrder.created_at <= last_thirty_mins_end).count()
    last_thirdy_mins_total_count = PayOrder.query.filter(PayOrder.channel_id == channel_id).filter(
        PayOrder.created_at >= last_thirty_mins_start).filter(PayOrder.created_at <= last_thirty_mins_end).count()

    if total_count > 0:
        rate = float(success_count) / float(total_count)
    else:
        rate = 0

    if last_thirdy_mins_total_count > 0:
        last_thirdy_mins_rate = float(last_thirdy_mins_success_count) / float(last_thirdy_mins_total_count)
    else:
        last_thirdy_mins_rate = 0
    decrease_rate = rate - last_thirdy_mins_rate

    channel = PayOrder.query.filter(PayOrder.channel_id == channel_id).first()
    if success_count == 0:
        msg = u'{}商户，   支付形式:{}，   通道id:{}，   持续30min无成功订单.'.format(MONITOR_MCH[str(channel.mch_id)], channel.service,
                                                                    channel.channel_id)
        _LOGGER.info(
            u'{}商户，   支付形式:{}，   通道id:{}，   持续30min无成功订单'.format(MONITOR_MCH[str(channel.mch_id)], channel.service,
                                                                 channel.channel_id))

        return str(msg)
    else:
        if float(decrease_rate) < float(-0.05):
            msg = u'{}商户，   支付形式:{}，   通道id:{}，   成功率近30min低于该项目该支付形式上一30min 5%，   近30min成功率:{}%，   上30min成功率:{}%.'.format(
                MONITOR_MCH[str(channel.mch_id)],
                channel.service,
                channel.channel_id,
                '%.2f' % (rate * 100.0),
                '%.2f' % (last_thirdy_mins_rate * 100.0))
            _LOGGER.info(u'{}商户，   支付形式:{}，   通道id:{}，   '
                         u'成功率近30min低于该项目该支付形式上一30min 5%，   近30min成功率:{}%，   上30min成功率:{}%.'.format(
                MONITOR_MCH[str(channel.mch_id)],
                channel.service,
                channel.channel_id,
                '%.2f' % (rate * 100.0),
                '%.2f' % (last_thirdy_mins_rate * 100.0)))

            return str(msg)


def _check_recently_one_hour_successful_rate(service):
    # 最近一小时的整体成功率，   定额支付宝成功率，   非定额支付宝成功率，   hydra成功率，   
    # 云闪付成功率，   非定额微信支付成功率，   定额微信支付成功率，   银联支付成功率，   QQ钱包支付成功率，   线下银行卡转账成功率
    start = datetime.utcnow() + timedelta(minutes=-60)
    success_count = PayOrder.query.filter(PayOrder.service == service).filter(
        PayOrder.status == PAY_STATUS.SUCC). \
        filter(PayOrder.created_at >= start).count()
    total_count = PayOrder.query.filter(PayOrder.service == service).filter(
        PayOrder.created_at >= start).count()
    if total_count > 0:
        rate = float(success_count) / float(total_count)
    else:
        rate = 0
    msg = u'支付形式:{}  近一小时成功率:{}%,  成功订单数:{},  总订单数:{}.'.format(service, '%.2f' % (rate * 100.0), success_count,
                                                               total_count)
    _LOGGER.info(u'支付形式:{}  近一小时成功率:{}%,  成功订单数:{},  总订单数:{}.'.format(service, '%.2f' % (rate * 100.0), success_count,
                                                                      total_count))
    return str(msg)


def _check_decrease_trend(service, mch_id):
    # 每30分钟支付形式告警。同比上一个30分钟下跌幅度告警（附上项目、支付形式、下跌幅度、成功率、订单数），   如该支付形式没有可用通道，   直接告警。
    last_thirty_mins_start = datetime.utcnow() + timedelta(minutes=-60)
    last_thirty_mins_end = datetime.utcnow() + timedelta(minutes=-30)

    success_count = PayOrder.query.filter(PayOrder.service == service).filter(
        PayOrder.status == PAY_STATUS.SUCC).filter(PayOrder.created_at >= last_thirty_mins_end).filter(
        PayOrder.mch_id == mch_id).count()
    total_count = PayOrder.query.filter(PayOrder.service == service).filter(
        PayOrder.created_at >= last_thirty_mins_end).filter(
        PayOrder.mch_id == mch_id).count()

    last_thirdy_mins_success_count = PayOrder.query.filter(PayOrder.service == service).filter(
        PayOrder.status == PAY_STATUS.SUCC).filter(PayOrder.created_at >= last_thirty_mins_start).filter(
        PayOrder.created_at <= last_thirty_mins_end).filter(PayOrder.mch_id == mch_id).count()
    last_thirdy_mins_total_count = PayOrder.query.filter(PayOrder.service == service).filter(
        PayOrder.created_at >= last_thirty_mins_start).filter(PayOrder.created_at <= last_thirty_mins_end).filter(
        PayOrder.mch_id == mch_id).count()

    if total_count > 0:
        rate = float(success_count) / float(total_count)
    else:
        rate = 0

    if last_thirdy_mins_total_count > 0:
        last_thirdy_mins_rate = float(last_thirdy_mins_success_count) / float(last_thirdy_mins_total_count)
    else:
        last_thirdy_mins_rate = 0

    decrease_rate = rate - last_thirdy_mins_rate

    msg = u'{}商户, 支付形式:{}, 同比上一个30分钟下跌幅度:{}%, ' \
          u'成功率:{}%, 成功订单数:{}, 总订单数:{}.'.format(MONITOR_MCH[str(mch_id)], service,
                                                '%.2f' % (decrease_rate * 100.0),
                                                '%.2f' % (rate * 100.0),
                                                success_count,
                                                total_count)
    _LOGGER.info(
        u'{}商户, 支付形式:{}, 同比上一个30分钟下跌幅度:{}%, '
        u'成功率:{}%, 成功订单数:{}, 总订单数:{}.'.format(MONITOR_MCH[str(mch_id)], service,
                                              '%.2f' % (decrease_rate * 100.0),
                                              '%.2f' % (rate * 100.0),
                                              success_count,
                                              total_count))
    return msg


def _check_not_available_channel_service():
    '''
    :return: 返回商户哪些支付形式没有可用通道
    '''

    avaliable_chns_all = Channel.query.filter(Channel.status == 1).filter(Channel.service_name != 'test').all()
    risk_channels = []
    unavailable_channels = []
    # 处于风控状态渠道列表
    for channel in avaliable_chns_all:
        if _check_channel_is_risk(channel.id, json.loads(channel.info)):
            risk_channels.append(channel)

    # 找出商户所有渠道都不可用的服务列表
    for mch in MONITOR_MCH.keys():
        for service_name in AVAILABLE_SERVICE_MAP:
            avaliable_chns_searching_by_mch_and_service = Channel.query.filter(Channel.status == 1).filter(
                Channel.service_name == service_name).filter(Channel.mch_id == mch).all()
            # 如果根据商户号与服务名查询结果为空，   表示该商户的该服务没有可用渠道
            if len(avaliable_chns_searching_by_mch_and_service) == 0:
                unavailable_channels.append((mch, service_name))
            else:
                # 如果根据商户号与服务名查询结果全部处于风控状态，   表示该商户的该服务没有可用渠道
                if set(avaliable_chns_searching_by_mch_and_service).issubset(risk_channels):
                    unavailable_channels.append((mch, service_name))
    msg_all = []
    if len(unavailable_channels) > 0:
        for item in unavailable_channels:
            _LOGGER.info(u'{}商户, 支付形式:{}, 没有可用通道.'.format(MONITOR_MCH[item[0]], item[1]))
            msg = u'{}商户, 支付形式:{}, 没有可用通道.'.format(MONITOR_MCH[item[0]], item[1])
            msg_all.append(str(msg))

    return msg_all


def _check_channel_is_risk(channel_id, chn_info):
    # 检查累积充值风控
    try:
        if 'incr_risk' in chn_info:
            daily_stats, hourly_stats, minutely_stats = redis_cache.get_chn_amount(channel_id)
            risk_conf = chn_info['incr_risk']
            if 'daily' in risk_conf:
                daily_conf = risk_conf['daily']
                amount, count = daily_conf.get('amount'), daily_conf.get('count', 1000000)
                if amount and int(daily_stats.get('amount', 0)) >= amount:
                    raise err.ReachLimit('chn reached daily amount limit, %s' % channel_id)
                if count and int(daily_stats.get('count', 0)) >= count:
                    raise err.ReachLimit('chn reached daily count limit, %s' % channel_id)
            if 'hourly' in risk_conf:
                hourly_conf = risk_conf['hourly']
                amount, count = hourly_conf.get('amount'), hourly_conf.get('count', 1000000)
                if amount and int(hourly_stats.get('amount', 0)) >= amount:
                    raise err.ReachLimit('chn reached hourly amount limit, %s' % channel_id)
                if count and int(hourly_stats.get('count', 0)) >= count:
                    raise err.ReachLimit('chn reached hourly count limit, %s' % channel_id)
            if 'minutely' in risk_conf:
                minutely_conf = risk_conf['minutely']
                amount, count = minutely_conf.get('amount'), minutely_conf.get('count', 1000000)
                if amount and int(minutely_stats.get('amount', 0)) >= amount:
                    raise err.ReachLimit('chn reached minutely amount limit, %s' % channel_id)
                if count and int(minutely_stats.get('count', 0)) >= count:
                    raise err.ReachLimit('chn reached minutely count limit, %s' % channel_id)
    except Exception as e:
        _LOGGER.warn('check incr_risk warn, %s', e)
        return True
    return False


def _send_tg(msg_all):
    if sys.getsizeof(msg_all) > 4000:
        # 分段分数
        times = int(sys.getsizeof(msg_all) / 4000 + 1)
        # 每段的长度
        int(len(msg_all) / times)
        i = 0
        while i < times:
            telegram.send_channel_text_msg_tele(_TG_FINANCIAL_RISK, msg_all[
                                                                    i * int(len(msg_all) / times):int(
                                                                        len(msg_all) / times) + i * int(
                                                                        len(msg_all) / times)])
            _LOGGER.info("msg is: %s", msg_all[
                                       i * int(len(msg_all) / times):int(len(msg_all) / times) + i * int(
                                           len(msg_all) / times)])
            i += 1
    else:
        telegram.send_channel_text_msg_tele(_TG_FINANCIAL_RISK, msg_all)


class Command(BaseCommand):
    def handle(self, **options):
        try:
            chns = Channel.query.filter(Channel.status == 1).filter(
                Channel.service_name.in_(AVAILABLE_SERVICE_MAP)).filter(
                Channel.mch_id.in_(MONITOR_MCH.keys())).all()

            # 返回商户哪些支付形式没有可用通道
            msg_all_not_available_channel_service = _check_not_available_channel_service()
            msg_not_available_channel_service = ''
            for i in msg_all_not_available_channel_service:
                msg_not_available_channel_service += i + '\n'
            _send_tg('\n' + u'【title: 无可用通道支付形式商户告警】' + '\n' + msg_not_available_channel_service)

            # 每30分钟支付形式告警。同比上一个30分钟下跌幅度告警（附上项目、支付形式、下跌幅度、成功率、订单数），   如该支付形式没有可用通道，   直接告警
            msg_all_decrease_trend = []
            for mch_id in MONITOR_MCH.keys():
                for service_name in AVAILABLE_SERVICE_MAP:
                    msg_decrease_trend = _check_decrease_trend(service_name, mch_id)
                    msg_all_decrease_trend.append(msg_decrease_trend)

            msg_decrease_trend = ''
            for i in msg_all_decrease_trend:
                msg_decrease_trend += i + '\n'
            _send_tg('\n' + u'【title: 同比上一个30分钟下跌幅度告警】' + '\n' + msg_decrease_trend)

            msg_all_recently_30_mins_risk_report = []
            for channel in chns:
                # 每30分钟指定通道告警。同比上一个30分钟的异常告警（附上项目、支付形式、通道id名称、异常数据和原因）
                # 1.持续30min无成功订单
                # 2.成功率近30min低于该项目该支付形式平均值5%
                msg_recently_30_mins_risk_report = _check_recently_30_mins_risk_report(channel.id)
                msg_all_recently_30_mins_risk_report.append(msg_recently_30_mins_risk_report)
            # _LOGGER.info("msg_all_recently_30_mins_risk_report is: %s", msg_all_recently_30_mins_risk_report)

            msg = ''
            while None in msg_all_recently_30_mins_risk_report:
                msg_all_recently_30_mins_risk_report.remove(None)
            msg_all_recently_30_mins_risk_report_sorted = sorted(msg_all_recently_30_mins_risk_report,
                                                                 key=lambda j: len(j))
            for i in msg_all_recently_30_mins_risk_report_sorted:
                msg += i + '\n'
            _send_tg('\n' + u'【title: 每30min指定通道告警】' + '\n' + msg)

            # 最近一小时的整体成功率，   定额支付宝成功率，   非定额支付宝成功率，   hydra成功率，
            # 云闪付成功率，   非定额微信支付成功率，   定额微信支付成功率，   银联支付成功率，   QQ钱包支付成功率，   线下银行卡转账成功率
            chns_check_recently_one_hour_successful_rate = Channel.query.filter(Channel.status == 1).filter(
                Channel.service_name.in_(AVAILABLE_SERVICE_MAP)).all()
            available_channels_name = []
            for channel in chns_check_recently_one_hour_successful_rate:
                available_channels_name.append(channel.service_name)
            msg_all = []
            for service_name in set(available_channels_name):
                msg = _check_recently_one_hour_successful_rate(service_name)
                msg_all.append(msg)
            msg = ''
            for i in msg_all:
                msg += i + '\n'
            _send_tg('\n' + u'【title: 最近一小时各支付形式整体成功率】' + '\n' + msg)

        except Exception as e:
            _LOGGER.exception("monitor pay error %s" % e)
